#ifndef A8_XVALUE_H
#define A8_XVALUE_H

#include <stdlib.h>
#include <string>
#include <vector>
#ifdef WIN32
#include <comutil.h>
#endif
#include <string.h>
#include <a8/compat.hpp>
#include <stdio.h>
#include <assert.h>

namespace a8
{
    const int XVT_NULL		= 0;
    const int XVT_INT		= 1;
    const int XVT_UINT		= 2;
    const int XVT_INT64		= 3;
    const int XVT_UINT64	= 4;
    const int XVT_STRING	= 5;
    const int XVT_FLOAT		= 6;
	const int XVT_WSTRING   = 7;
	const int XVT_USERDATA  = 8;

    class XValue
    {
    public:
        XValue()
        {
            mType = XVT_INT;
            mValue.mIntValue = 0;
            mDataSize = 0;
        }

        ~XValue()
        {
            if (mType == XVT_STRING && mValue.mStrValue){
                free(mValue.mStrValue);                
			}else if (mType == XVT_WSTRING && mValue.mWStrValue){
				free(mValue.mWStrValue);
			}
			mValue.mIntValue = 0;
        }

		XValue(const XValue& xv)
		{			
			if (xv.type() == XVT_STRING){
				SetDynData(xv.mValue.mStrValue, xv.mDataSize);
			}else if (xv.type() == XVT_WSTRING){
				Set(xv.mValue.mWStrValue);
			}else{
				mType = xv.mType;
				mValue = xv.mValue;
			}					
		}

        XValue(int v)
        {
            mType = XVT_NULL;
            Set(v);
        }

		XValue(unsigned long v)
		{
			mType = XVT_NULL;
			Set(v);
		}

		XValue(double v)
		{
			mType = XVT_FLOAT;
			Set(v);
		}

        XValue(const char* v)
        {
            mType = XVT_NULL;
            Set(v);
        }

		XValue(const std::string& v)
		{
			mType = XVT_NULL;
			SetDynData(v.data(), v.size());
		}

		XValue(std::string& v)
		{
			mType = XVT_NULL;
			Set(v.c_str());
			SetDynData(v.data(), v.size());
		}
	  
		XValue(long long v)
		{
			mType = XVT_NULL;
			Set(v);
		}

		XValue(unsigned long long v)
		{
			mType = XVT_NULL;
			Set(v);
		}

        void Set(int v)
        {		  
            OnReset();
            mType = XVT_INT;
            mValue.mIntValue = v;
        }

        void Set(unsigned int v)
        {
            OnReset();
            mType = XVT_UINT;
            mValue.mIntValue = v;
        }

		void Set(unsigned long v)
		{		  
			OnReset();
			mType = XVT_UINT;
			mValue.mIntValue = v;
		}

        void Set(long long v)
        {
            OnReset();
            mType = XVT_INT64;
            mValue.mIntValue = v;
        }
	  
        void Set(unsigned long long v)
        {
            OnReset();
            mType = XVT_UINT64;
            mValue.mIntValue = v;
        }

        void Set(const char* v)
        {
			if (v){
				SetDynData(v, strlen(v));
			}else{
				SetDynData("", 0);
			}
        }

		void Set(const wchar_t* v)
		{
			OnReset();
			mType = XVT_WSTRING;
			int len = v ? wcslen(v) : 0;
			if (len > 0){
				mValue.mWStrValue = (wchar_t*)malloc((len + 2) * 2);
#ifdef WIN32
				wcscpy_s(mValue.mWStrValue, len + 1, v);
#else
				wcscpy(mValue.mWStrValue, v);
#endif
			}else
				mValue.mWStrValue = NULL;
		}

		void SetDynData(const char* v, unsigned int len)
		{
            OnReset();
            mType = XVT_STRING;
			mDataSize = len;
            if (len > 0){
                mValue.mStrValue = (char*)malloc(len + 1);
                memmove(mValue.mStrValue, v, len);
				mValue.mStrValue[len] = '\0';
            }else
				mValue.mStrValue = NULL;
		}

		void SetUserData(void *userdata, int datasize)
		{
			OnReset();
			mType = XVT_USERDATA;
			mDataSize = datasize;
			mValue.mUserData = userdata;
		}
    
        void Set(double v)
        {
            OnReset();
            mType = XVT_FLOAT;
            mValue.mFloatValue = v;
        }

        void SetNull()
        {
            OnReset();
            mType = XVT_NULL;
            mValue.mIntValue = 0;
        }

#ifdef WIN32
        void SetVairent(_variant_t& v)
        {
            OnReset();
            switch (v.vt){
            case VT_EMPTY:
                SetNull();  
                break;
			case VT_NULL:
				SetNull();  
				break;
            case VT_I2:
                Set(v.iVal);
                break;
            case VT_I4:
                Set(v.lVal);
                break;
            case VT_R4:
                Set((double)v.fltVal);
                break;;
            case VT_R8:
                Set(v.dblVal);
                break;;
            case VT_CY:
                Set(v.cyVal.int64);
                break;
            case VT_DATE:
                Set(v.date);
                break;
            case VT_BSTR:
                 Set(v.bstrVal);
                 break;
            case VT_BOOL:
                Set(v.boolVal ? 1 : 0);
                break;
            case VT_I1:
                Set(v.cVal);
                break;
            case VT_UI1:
                Set((unsigned int)v.bVal);
                break;
            case VT_UI2:
                Set((unsigned int)v.uiVal);
                break;
            case VT_UI4:
                Set((unsigned int)v.ulVal);
                break;
            case VT_I8:
                Set((long long)v.llVal);
                break;
            case VT_UI8:
                Set((unsigned long long)v.ullVal);
                break;
            case VT_INT:
                Set(v.intVal);
                break;
            case VT_UINT:
                Set(v.uintVal);
                break;
            case VT_LPSTR:
                {
                    if (v.pcVal)
                        Set(v.pcVal);
                    else
                        Set("");
                    break;
                }
			case VT_DECIMAL:
				Set((long long)v);
				break;
                /*case VT_LPWSTR:
		  case VT_BLOB:
		  case VT_STREAM:*/
			case VT_ARRAY | VT_UI1:
				{
					long lbound = 0;
					long ubound = 0;
					SafeArrayGetLBound(v.parray, 1, &lbound);
					SafeArrayGetUBound(v.parray, 1, &ubound);
					long size = ubound - lbound + 1;
					printf("lbound:%d  ubound:%d\n", lbound, ubound);

					BYTE* pData = NULL;
					SafeArrayAccessData(v.parray, (void**)&pData);
					if (size > 0){
						SetDynData((const char *)pData, size);
					}
					else{
						SetNull();
					}
					SafeArrayUnaccessData(v.parray);
				}
				break;
            default:
				printf("unknow _variant_t type:%d \n", v.vt);
                assert(0);
                SetNull();
            }
        }
#endif

        int type() const 
        {
            return mType;
        }

		bool IsNull()
		{
			return mType == XVT_NULL;
		}

		bool IsNumber()
		{
			return mType == XVT_INT || mType == XVT_UINT || mType == XVT_INT64 ||
				mType == XVT_UINT64 || mType == XVT_FLOAT;
		}

		bool IsString()
		{
			return mType == XVT_STRING || mType == XVT_WSTRING;
		}

        int GetInt()
        {
            return (int)GetInt64();		  
        }

        unsigned int GetUInt()
        {
            return (unsigned int)GetUInt64();		
        }

        long long GetInt64()
        {
            switch(mType){
            case XVT_FLOAT:
                return (long long)mValue.mFloatValue;
                break;
            case XVT_STRING:
#ifdef WIN32
					return mValue.mStrValue ? _atoi64(mValue.mStrValue) : 0;			
#else				
					return mValue.mStrValue ? strtoll(mValue.mStrValue, NULL, 10) : 0;
#endif
                break;	
			case XVT_WSTRING:
#ifdef WIN32
					return _atoi64(GetString().c_str());							
#else				
					return strtoll(GetString().c_str(), NULL, 10);				
#endif
				break;
            default:
                return mValue.mIntValue;
            }	
        }

        unsigned long long GetUInt64()
        {
            switch(mType){
            case XVT_FLOAT:
                return (unsigned long long)mValue.mFloatValue;
                break;
            case XVT_STRING:
                {
                    unsigned long long ui64 = 0;
					if (mValue.mStrValue){		
#ifdef _WIN32			
						sscanf_s(mValue.mStrValue, "%I64u", &ui64);						
#else
						sscanf(mValue.mStrValue, "%llu", &ui64);						
#endif
					}
                    return ui64;
                    break;					
                }
			case XVT_WSTRING:
				{
					unsigned long long ui64 = 0;
					if (mValue.mStrValue){
#ifdef WIN32
						sscanf_s(GetString().c_str(), "%I64u", &ui64);	
#else
						sscanf(GetString().c_str(), "%I64u", &ui64);
#endif
					}
					return ui64;
					break;
				}
            default:
                return (unsigned long long)mValue.mIntValue;
            }
        }

        double GetDouble()
        {
            switch(mType){
            case XVT_FLOAT:
                return mValue.mFloatValue;
                break;
            case XVT_STRING:
                {
                    double dwV = 0;
					if (mValue.mStrValue){
						if (strstr(mValue.mStrValue, "-")){
#ifdef WIN32						
							_variant_t varDate(GetWString().c_str()); 				
							varDate.ChangeType(VT_DATE); 
							dwV = varDate.date;
#else	
							sscanf(mValue.mStrValue, "%lf", &dwV);					
#endif
						}else{			
#ifdef WIN32
							sscanf_s(mValue.mStrValue, "%lf", &dwV);
#else
							sscanf(mValue.mStrValue, "%lf", &dwV);
#endif
						}
					}
                    return dwV;
                    break;					
                }
			case XVT_WSTRING:
				{
					double dwV = 0;
					if (mValue.mWStrValue){
						if (wcsstr(mValue.mWStrValue, L"-")){
#ifdef WIN32
							_variant_t varDate(GetWString().c_str()); 					
							varDate.ChangeType(VT_DATE); 
							dwV = varDate.date;
#else	
							assert(false);
#endif
						}else{
#ifdef WIN32
							sscanf_s(GetString().c_str(), "%lf", &dwV);
#else
							sscanf(GetString().c_str(), "%lf", &dwV);	
#endif
						}
					}
					return dwV;
					break;					
				}
            default:
                return (double)GetInt64();
            }
        }

        std::string GetString() const 
        {
			const int BUF_LEN = 56;
            char buf[BUF_LEN + 1];
            switch(mType){
            case XVT_STRING:
                return mValue.mStrValue ? std::string(mValue.mStrValue, mDataSize) : std::string();
                break;
			case XVT_WSTRING:
				return ws2s(GetWString());
				break;
            case XVT_FLOAT:
                {
#ifdef WIN32
                    sprintf_s(buf, "%.12f", mValue.mFloatValue);
#else
					sprintf(buf, "%.12f", mValue.mFloatValue);
#endif
                    return std::string(buf);
                    break;					
                }
            case XVT_INT:
            case XVT_INT64:
                {
                    XItoa(mValue.mIntValue, buf, BUF_LEN, 10);
                    return std::string(buf);
                    break;					
                }
            case XVT_UINT:
            case XVT_UINT64:
                {
                    XItoa((unsigned long long)mValue.mIntValue, buf, BUF_LEN, 10);
                    return std::string(buf);
                    break;					
                }
            default:
                return std::string();
            }
        }

		std::wstring GetWString() const 
		{
			if (mType == XVT_WSTRING){
                return mValue.mWStrValue ? std::wstring(mValue.mWStrValue) : std::wstring();
			}else{
				return s2ws(GetString());
			}		
		}

		void* GetUserData()
		{
			return mType == XVT_USERDATA ? mValue.mUserData : NULL;
		}

		operator bool()
		{
			return GetInt() != 0;
		}

		operator unsigned char()
		{
			return GetUInt();
		}

		operator short()
		{
			return GetInt();
		}

		operator unsigned short()
		{
			return GetUInt();
		}

		operator int()
		{
			return GetInt();
		}

		operator unsigned int()
		{
			return GetUInt();
		}

		operator long()
		{
			return GetInt();
		}

		operator unsigned long()
		{
			return GetUInt();
		}

		operator double()
		{
			return GetDouble();
		}

		operator long long()
		{
			return GetInt64();
		}

		operator unsigned long long()
		{
			return GetUInt64();
		}

		operator std::string ()
		{
			return GetString();
		}

		bool IsUserData()
		{
			return mType == XVT_USERDATA;
		}

    private:
        unsigned char mType;
        union{
            long long mIntValue;
            double mFloatValue;
            char* mStrValue;
			wchar_t* mWStrValue;
			void* mUserData;
        } mValue;
		unsigned int mDataSize;

        void OnReset()
        {
            if (mType == XVT_STRING && mValue.mStrValue){
                free(mValue.mStrValue);                
			}else if (mType == XVT_WSTRING && mValue.mWStrValue){
				free(mValue.mWStrValue);
			}
			mValue.mIntValue = 0;
			mDataSize = 0;
        }
    };
	
	/*
	std::vector<a8::XValue> CreateXValueArray(
											  )
	{
	}

	std::vector<std::pair<std::string, a8::XValue> > CreateXValuePairs()
	{
	}*/
#include <a8/inc/xvalue.inl>
}

#endif
